#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/err.h>
#include <linux/usb/composite.h>

#include "g_zero.h"
#include "u_f.h"

static const char longname[] = "Gadget Zero";

static struct usb_zero_options gzero_options = {
	.bulk_buflen = GZERO_BULK_BUFLEN,
};

#define DRIVER_VENDOR_NUM	0x0525		/* NetChip */
#define DRIVER_PRODUCT_NUM	0xa4a0		/* Linux-USB "Gadget Zero" */

static struct usb_device_descriptor device_desc = {
	.bLength =		sizeof device_desc,
	.bDescriptorType =	USB_DT_DEVICE,

	.bcdUSB =		cpu_to_le16(0x0200),
	.bDeviceClass =		USB_CLASS_VENDOR_SPEC,

	.idVendor =		cpu_to_le16(DRIVER_VENDOR_NUM),
	.idProduct =		cpu_to_le16(DRIVER_PRODUCT_NUM),
	.bNumConfigurations =	1,
};

static struct usb_configuration loopback_driver = {
	.label          = "loopback",
	.bConfigurationValue = 2,
	.bmAttributes   = USB_CONFIG_ATT_SELFPOWER,
	/* .iConfiguration = DYNAMIC */
};

static struct usb_function *func_lb;

struct f_loopback {
	struct usb_function	function;

	struct usb_ep		*in_ep;
	struct usb_ep		*out_ep;

	unsigned                buflen;
};

static struct usb_endpoint_descriptor hs_loop_source_desc = {
	.bLength =		USB_DT_ENDPOINT_SIZE,
	.bDescriptorType =	USB_DT_ENDPOINT,

	.bEndpointAddress =	USB_DIR_IN,
	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
	.wMaxPacketSize =	cpu_to_le16(512),
};

static struct usb_endpoint_descriptor hs_loop_sink_desc = {
	.bLength =		USB_DT_ENDPOINT_SIZE,
	.bDescriptorType =	USB_DT_ENDPOINT,

	.bEndpointAddress =	USB_DIR_OUT,
	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
	.wMaxPacketSize =	cpu_to_le16(512),
};

static struct usb_interface_descriptor loopback_intf = {
	.bLength =		sizeof(loopback_intf),
	.bDescriptorType =	USB_DT_INTERFACE,

	.bNumEndpoints =	2,
	.bInterfaceClass =	USB_CLASS_VENDOR_SPEC,
	/* .iInterface = DYNAMIC */
};

static struct usb_descriptor_header *hs_loopback_descs[] = {
	(struct usb_descriptor_header *) &loopback_intf,
	(struct usb_descriptor_header *) &hs_loop_source_desc,
	(struct usb_descriptor_header *) &hs_loop_sink_desc,
	NULL,
};

static inline struct f_loopback *func_to_loop(struct usb_function *f)
{
	return container_of(f, struct f_loopback, function);
}

static void loopback_complete(struct usb_ep *ep, struct usb_request *req)
{
	pr_err("%s, %d. \n", __func__, __LINE__);
	struct f_loopback	*loop = ep->driver_data;
	struct usb_composite_dev *cdev = loop->function.config->cdev;
	int			status = req->status;

	switch (status) {
	case 0:				/* normal completion? */
		if (ep == loop->out_ep) {
			/*
			 * We received some data from the host so let's
			 * queue it so host can read the from our in ep
			 */
			struct usb_request *in_req = req->context;

			in_req->zero = (req->actual < req->length);
			in_req->length = req->actual;
			ep = loop->in_ep;
			req = in_req;
		} else {
			/*
			 * We have just looped back a bunch of data
			 * to host. Now let's wait for some more data.
			 */
			req = req->context;
			ep = loop->out_ep;
		}

		/* queue the buffer back to host or for next bunch of data */
		status = usb_ep_queue(ep, req, GFP_ATOMIC);
		if (status == 0) {
			return;
		} else {
			ERROR(cdev, "Unable to loop back buffer to %s: %d\n",
			      ep->name, status);
			goto free_req;
		}

		/* "should never get here" */
	default:
		ERROR(cdev, "%s loop complete --> %d, %d/%d\n", ep->name,
				status, req->actual, req->length);
		/* FALLTHROUGH */

	/* NOTE:  since this driver doesn't maintain an explicit record
	 * of requests it submitted (just maintains qlen count), we
	 * rely on the hardware driver to clean up on disconnect or
	 * endpoint disable.
	 */
	case -ECONNABORTED:		/* hardware forced ep reset */
	case -ECONNRESET:		/* request dequeued */
	case -ESHUTDOWN:		/* disconnect from host */
free_req:
	pr_err("%s, %d. \n", __func__, __LINE__);
		usb_ep_free_request(ep == loop->in_ep ?
				    loop->out_ep : loop->in_ep,
				    req->context);
		free_ep_req(ep, req);
		return;
	}
}

static int alloc_requests(struct usb_composite_dev *cdev,
			  struct f_loopback *loop)
{
	struct usb_request *in_req, *out_req;
	int result = 0;

	/*
	 * allocate a bunch of read buffers and queue them all at once.
	 * we buffer at most 'qlen' transfers; We allocate buffers only
	 * for out transfer and reuse them in IN transfers to implement
	 * our loopback functionality
	 */
	in_req = usb_ep_alloc_request(loop->in_ep, GFP_ATOMIC);
	out_req = alloc_ep_req(loop->out_ep, 0, loop->buflen);

	/* 当任务加入 ep 的任务队列成功后(非执行成功, 执行成功会触发中断), 执行loopback_complete,
	 * 当该任务经由usb设备控制器处理完成时, 会触发中断,
	 * 执行ep队列的下一个任务.*/
	in_req->complete = loopback_complete;
	out_req->complete = loopback_complete;

	in_req->buf = out_req->buf;
	/* length will be set in complete routine */
	in_req->context = out_req;
	out_req->context = in_req;

	/* 将out_req任务, 加入loop->out_ep的任务队列, 
	 * 符合先进先处理原则.*/
	result = usb_ep_queue(loop->out_ep, out_req, GFP_ATOMIC);

	return 0;
}

static int enable_endpoint(struct usb_composite_dev *cdev,
			   struct f_loopback *loop, struct usb_ep *ep)
{
	int	result;

	result = config_ep_by_speed(cdev->gadget, &(loop->function), ep);

	result = usb_ep_enable(ep);
	ep->driver_data = loop;
	result = 0;

	return result;
}

static void loopback_disable(struct usb_function *f)
{
	struct f_loopback	*loop = func_to_loop(f);
	struct usb_composite_dev	*cdev;

	cdev = loop->function.config->cdev;
	usb_ep_disable(loop->in_ep);
	usb_ep_disable(loop->out_ep);
}

static int loopback_set_alt(struct usb_function *f,
		unsigned intf, unsigned alt)
{
	struct f_loopback	*loop = func_to_loop(f);
	struct usb_composite_dev *cdev = f->config->cdev;

	enable_endpoint(cdev, loop, loop->in_ep);
	enable_endpoint(cdev, loop, loop->out_ep);

	/* 给端点分配任务. */
	alloc_requests(cdev, loop);

	return 0;
}

static int loopback_bind(struct usb_configuration *c, struct usb_function *f)
{
	struct usb_composite_dev *cdev = c->cdev;
	struct f_loopback	*loop = func_to_loop(f);
	int			id;
	int ret;

	/* allocate interface ID(s) */
	id = usb_interface_id(c, f);
	loopback_intf.bInterfaceNumber = id;

	/* usb_ep_autoconfig() 根据端点描述符信息, 从ep列表里,
	 * 找到匹配的合适的ep, 将其返回.*/
	loop->in_ep = usb_ep_autoconfig(cdev->gadget, &hs_loop_source_desc);

	loop->out_ep = usb_ep_autoconfig(cdev->gadget, &hs_loop_sink_desc);

	/* usb_assign_descriptors() 将接口描述符和端点描述符信息, 
	 * 集合至 f, 即func_lb.*/
	ret = usb_assign_descriptors(f, NULL, hs_loopback_descs, NULL);
	return 0;
}

static void lb_free_func(struct usb_function *f)
{
	usb_free_all_descriptors(f);
	kfree(func_to_loop(f));
}

static struct usb_function *loopback_alloc(void)
{
	struct f_loopback	*loop;

	loop = kzalloc(sizeof *loop, GFP_KERNEL);

	loop->buflen = gzero_options.bulk_buflen;

	loop->function.bind = loopback_bind;
	loop->function.set_alt = loopback_set_alt;
	loop->function.disable = loopback_disable;

	loop->function.free_func = lb_free_func;

	return &loop->function;
}

static int gm_zero_bind(struct usb_composite_dev *cdev)
{
	int			status;

	func_lb = loopback_alloc();

	/* Register primary, then secondary configuration.  Note that
	 * SH3 only allows one config...
	 */
	/* loopback_driver 包含usb设备的配置描述符信息,
	 * usb_add_function() 将 描述符信息集合至 cdev*/
	usb_add_config_only(cdev, &loopback_driver);

	/* func_lb 包含 接口描述符和端点描述符信息,
	 * usb_add_function() 将 信息集合至 loopback_driver*/
	status = usb_add_function(&loopback_driver, func_lb);

	return 0;
}

static int zero_unbind(struct usb_composite_dev *cdev)
{
	usb_put_function(func_lb);

	return 0;
}

static struct usb_string strings_dev[] = {
	[0].s = "",
	{  }			/* end of list */
};

static struct usb_gadget_strings stringtab_dev = {
	.strings	= strings_dev,
};

static struct usb_gadget_strings *dev_strings[] = {
	&stringtab_dev,
	NULL,
};

static struct usb_composite_driver zero_driver = {
	.name		= "zero",
	.dev		= &device_desc,
	.strings	= dev_strings,
	.max_speed	= USB_SPEED_HIGH,
	.bind		= gm_zero_bind,
	.unbind		= zero_unbind,
};

module_usb_composite_driver(zero_driver);

MODULE_AUTHOR("David Brownell");
MODULE_LICENSE("GPL");
